"""
Copyright (c) 2022 Huawei Technologies Co.,Ltd.

openGauss is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:

          http://license.coscl.org.cn/MulanPSL2

THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
"""
"""
Case Type   : 端口故障类
Case Name   : 级联备重建时，级联备基础端口占用
Create At   : 2021/07/13
Owner       : @wan005
Description :
    1.级联备注入基础端口被占用故障
    ./cfe "inject rNet_port_conflict (port) values({port})"
    2.级联备重建
    gs_ctl build -D {dn1} -b full
    3.检查集群状态
    4.清除故障 恢复环境 再次重建
    ./cfe "clean rNet_port_conflict where port ={port}"
    gs_ctl start -D {dn1} -M standby
    gs_om -t restart
    gs_om -t status --detail
    5.运行TPCC
    6.检查数据一致性
Expect      :
    1.故障注入成功
    2.级联备重建失败
    3.数据库状态异常
    4.清除故障 恢复环境 再次重建成功
    5.运行成功
    6.数据一致
History     :
    Modified by @wan005 2021/8/4 : 优化故障清理断言和teardown
    Modified by @peilinqian 2021/10/20 : 级联备同步校验存在问题，不一致的情况返回none，增加循环等待
    Modified by @wan005 2022/1/11 : 优化nc安装及teardown
"""
import time
import unittest

from testcase.utils.ComThread import ComThread
from testcase.utils.Common import Common
from testcase.utils.CommonSH import CommonSH
from testcase.utils.CommonSH import RestartDbCluster
from testcase.utils.Constant import Constant
from testcase.utils.Logger import Logger
from yat.test import Node, macro


class ReliabilityPort(unittest.TestCase):
    nodes = ("PrimaryDbUser", "Standby1DbUser", "Standby2DbUser")

    @RestartDbCluster(*nodes)
    def setUp(self):
        self.log = Logger()
        self.primary_sh = CommonSH(self.nodes[0])
        self.primary_node = Node(self.nodes[0])

        self.constant = Constant()
        self.com = Common()
        self.primary_root_node = Node(node="PrimaryRoot")
        self.standby1_root_node = Node(node="Standby1Root")
        self.standby1_sh = CommonSH(self.nodes[1])
        self.log.info("setUp-实例化:ok")

        for i in range(3):
            result = self.com.get_sh_result(Node(self.nodes[i]),
                                            "nc --version")
            self.log.info(result)
            if not "Version" in result:
                result = self.com.get_sh_result(Node(self.nodes[i]),
                                                "yum -y install nc")
                self.log.info(result)
        self.log.info("setUp-安装nc:ok")

        self.log.info("Opengauss_Reliability_Port_Case004:开始执行")
        self.log.info("以级联备重建级联备并刷新配置文件")
        result = self.standby1_sh.executeGsctl("build",
                                               "server started",
                                               param="-M cascade_standby")
        self.assertTrue(result)
        result = self.standby1_sh.exec_refresh_conf()
        self.assertTrue(result)
        db_status = self.primary_sh.getDbClusterStatus("detail")
        self.log.info(db_status)
        self.assertTrue("S Cascade Normal" in db_status)
        result = self.standby1_sh.executeGsctl("stop", "server stopped")
        self.assertTrue(result)

        self.port = Node("PrimaryDbUser").db_port
        self.inject_cmd = f"rNet_port_conflict (port) values({self.port})"
        self.clean_cmd = f"rNet_port_conflict where port ={self.port}"

    def test_main(self):
        try:
            self.log.info("步骤1:级联备注入基础端口被占用故障")
            self.log.info("查看当前端口未被占用")
            shell_cmd = f"netstat -anp|grep {self.port}"
            self.log.info(shell_cmd)
            result = self.standby1_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertNotIn("gaussdb", result)

            self.log.info("步骤2:build级联备")
            sql = f"source {macro.DB_ENV_PATH};" \
                  f"gs_ctl build -D {macro.DB_INSTANCE_PATH} " \
                  f"-M cascade_standby"
            session1 = ComThread(self.com.get_sh_result,
                                 args=(Node("Standby1DbUser"), sql))
            session1.setDaemon(True)
            session1.start()

            self.log.info("注入故障")
            session2 = ComThread(self.com.cfe_inject,
                                 args=(self.standby1_root_node,
                                       self.inject_cmd))
            session2.setDaemon(True)
            session2.start()
            time.sleep(2)

            session2.join(30)
            result = session2.get_result()
            self.log.info(result)

            self.log.info("build级联备result")
            session1.join(60*30)
            build_result = session1.get_result()
            self.log.info("build_result:")
            self.log.info(build_result)
            self.assertIn(f"Port {self.port} is used", build_result)

            self.log.info("注入故障后查看当前端口被占用")
            result = self.standby1_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertIn(self.port, result)
            self.assertIn("0.0.0.0", result)
            self.assertIn("/nc", result)
            self.assertNotIn("gaussdb", result)

            self.log.info("步骤3:检查集群状态")
            db_status = self.primary_sh.getDbClusterStatus("detail")
            self.log.info(db_status)
            self.assertTrue("Degraded" in db_status)
            self.assertTrue("S Down" in db_status)
        finally:
            self.log.info("步骤4:清除故障 恢复环境")
            result = self.com.cfe_clean(self.standby1_root_node,
                                        self.clean_cmd)
            self.log.info(result)
            self.assertIn("successful execution", result)

            self.log.info("查看当前端口未被占用")
            shell_cmd = f"netstat -anp|grep {self.port}"
            self.log.info(shell_cmd)
            result = self.standby1_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertEqual("", result.strip())

        self.log.info("再次build级联备")
        result = self.standby1_sh.executeGsctl("build",
                                               "server started",
                                               param="-M cascade_standby")
        self.assertTrue(result)

        db_status = self.primary_sh.getDbClusterStatus("detail")
        self.log.info(db_status)
        self.assertTrue("S Cascade Normal" in db_status)

        self.log.info("步骤5:运行TPCC")
        result = self.com.startTPCC(self.primary_root_node, macro.TPCC_PATH)
        self.log.info(result)
        self.assertIn(self.constant.TPCC_SUCCESS_MSG, result)

        self.log.info('检查主备是否同步')
        time.sleep(10)
        res = self.primary_sh.check_location_consistency("primary", 2)
        self.assertTrue(res)
        for i in range(90):
            res = CommonSH(self.nodes[2]).check_cascade_standby_consistency()
            if res:
                break
            time.sleep(20)
        self.assertTrue(res)

        self.log.info('备2-级联备检查同步:ok')

        self.log.info('步骤6:检查数据一致性')
        nodes = (self.primary_node, Node(self.nodes[1]), Node(self.nodes[2]))
        flag = self.com.check_data_sample_by_all(r'\d', *nodes)
        self.assertTrue(flag)
        table_dict = self.com.format_sql_result(
            self.primary_sh.executDbSql(r'\d'))
        table_name = table_dict.get('Name')
        for name in table_name:
            select_sql = f'select count(*) from {name};'
            flag = self.com.check_data_sample_by_all(select_sql, *nodes)
            self.assertTrue(flag)

    def tearDown(self):
        self.log.info("环境清理")
        self.com.cfe_clean(self.standby1_root_node, self.clean_cmd)

        self.log.info("如果存在级联备 则恢复为正常备")
        db_status = self.primary_sh.getDbClusterStatus("detail")
        self.log.info(db_status)
        self.standby1_sh.executeGsctl("build",
                                      "server started",
                                      param="-b full")
        self.standby1_sh.exec_refresh_conf()

        db_status = self.primary_sh.getDbClusterStatus("detail")
        self.log.info(db_status)
        self.assertTrue("Degraded" in db_status or "Normal" in db_status)
        self.log.info("Opengauss_Reliability_Port_Case004:执行完成")
